package org.osll.tictactoe.transport.tcp.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import org.osll.tictactoe.Move;
import org.osll.tictactoe.engine.MoveImpl;
import org.osll.tictactoe.game.Game;
import org.osll.tictactoe.transport.tcp.ApponentMoveQuery;
import org.osll.tictactoe.transport.tcp.ApponentMoveResponse;
import org.osll.tictactoe.transport.tcp.ApponentNameQuery;
import org.osll.tictactoe.transport.tcp.ApponentNameResponse;
import org.osll.tictactoe.transport.tcp.DefaultQuery;
import org.osll.tictactoe.transport.tcp.ErrorResponse;
import org.osll.tictactoe.transport.tcp.LoginAcceptedResponse;
import org.osll.tictactoe.transport.tcp.LoginQuery;
import org.osll.tictactoe.transport.tcp.LoginRejectedResponse;
import org.osll.tictactoe.transport.tcp.MoveQuery;
import org.osll.tictactoe.transport.tcp.MoveResponse;
import org.osll.tictactoe.transport.tcp.OverResponse;
import org.osll.tictactoe.transport.tcp.ReflexiveTimeResponse;
import org.osll.tictactoe.transport.tcp.SizeResponse;
import org.osll.tictactoe.transport.tcp.StartedResponse;

public class GameServer implements Runnable {

	private class ServerThread extends SocketProcessor implements Runnable {

		private Socket socket = null;
		
		public ServerThread(Socket socket) {
			this.socket = socket;
		}
		@Override
		public void run() {
			for(;!Thread.currentThread().isInterrupted();) {
				DefaultQuery query = (DefaultQuery)read(socket);
				if(query==null)	{
					continue;
				}
				switch (query.getType()) {
				case LOGIN:
				{
					LoginAcceptedResponse resp = new LoginAcceptedResponse();
					LoginQuery q = (LoginQuery)query;
					try {
						controller.loginPlayer(q.getName(), q.getTeam());
					} catch (IllegalStateException e) {
						write(socket, new LoginRejectedResponse());
					}
					write(socket, resp);
					break;
				}
				case SIZE:
				{
					SizeResponse resp = new SizeResponse();
					resp.setSize(controller.getField().getSize());
					write(socket,resp);
					break;
				}
				case REFLEXIVE_TIME:
				{
					ReflexiveTimeResponse resp = new ReflexiveTimeResponse();
					resp.setMove(controller.getCurrentMove());
					write(socket,resp);
					break;
				}
				case MOVE:
				{
					MoveQuery q = (MoveQuery)query;
					Move move = new MoveImpl(q.getRow(),q.getCol(),q.getTeam());
					try {
						controller.makeMove(move);
						write(socket,new MoveResponse());
					} catch (IllegalStateException e) {
						ErrorResponse r = new ErrorResponse();
						r.setException(e);
						write(socket,r);
					}
					break;
				}
				case APPONENT_MOVE:
				{
					ApponentMoveResponse resp = new ApponentMoveResponse();
					ApponentMoveQuery q = (ApponentMoveQuery)query;
					try {
						resp.setMove(controller.getLastMove(q.getTeam()));
						write(socket,resp);
					} catch(IllegalStateException e) {
						ErrorResponse r = new ErrorResponse();
						r.setException(e);
						write(socket,r);
					}
					break;
				}
				case STARTED: 
				{
					StartedResponse resp = new StartedResponse();
					resp.setStarted(controller.isStarted());
					write(socket,resp);
					break;
				}
				case OVER:
				{
					OverResponse resp = new OverResponse();
					resp.setOver(controller.isOver());
					write(socket,resp);
					break;
				}
				case APPONENT_NAME:
				{
					ApponentNameResponse resp = new ApponentNameResponse();
					resp.setName(controller.getApponentName(((ApponentNameQuery)query).getTeam()));
					write(socket,resp);
					break;
				}
				default:
					break;
				}
			}
		}
		
	}
	
	private ServerSocket socket= null;
	private Game controller = null;
	
	public GameServer(Game controller) {
		this.controller = controller;
		try {
			socket = new ServerSocket(0);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	@Override
	public void run() {
		for(;;) {
			try {
				Socket client = socket.accept();
				new Thread(new ServerThread(client)).start();
			} catch (IOException e) {
				// TODO need test
				// думаю, что ничего страшного
			}
		}
	}

	public int getPort() {
		return socket.getLocalPort();
	}

}
